import re, getopt,sys

BASE2 = "01"
BASE10 = "0123456789"
def baseconv(number,fromdigits,todigits):
    if str(number)[0]=='-':
        number = str(number)[1:]
        neg=1
    else:
        neg=0

    # make an integer out of the number
    x=long(0)
    for digit in str(number):
       x = x*len(fromdigits) + fromdigits.index(digit)
    
    # create the result in base 'len(todigits)'
    res=""
    while x>0:
        digit = x % len(todigits)
        res = todigits[digit] + res
        x /= len(todigits)
    if neg:
        res = "-"+res

    return res

def dec2bin(num,str_len):
    temp_str=baseconv(num,BASE10,BASE2)
    return '0'*(str_len-len(temp_str))+temp_str

def formatcoe(data):
    header_text='memory_initialization_radix=2;\nmemory_initialization_vector=\n'
    """Take the input data and split it into 32 bit chunks, one of those per line"""
    cs=32
    assert(len(data)%cs==0)
    out_data=header_text
    chunks=len(data)/cs
    for i in range(chunks):
        if i==chunks-1:
            out_data+=((data[i*cs:(i+1)*cs])+';')
        else:
            out_data+=(data[i*cs:(i+1)*cs]+',\n')
    return out_data

commands={}
# Command format
# Key is the instruction name
# Type 'r': inst $a,$b,$c  : ['r',opcode,function] : [opcode,num[1],num[2],num[0],'0'*5,function]
# Type 'l': inst $a,$b(c)  : ['l',opcode] : [opcode,num[1],num[0],num[2]]
# Type 'k': inst $a,c      : ['k',opcode] : [opcode,num[0],'0'*5,num[1]]
# Type 'b': inst $a        : ['b',opcode] : [opcode,'0'*10,num[0]]
# Type 'n': inst           : ['n',opcode] : [opcode]
# Formats are 0=Non-existant 1=register 2=register(immediate index)
commands['add']=['r','000000','00000']
commands['sub']=['r','000000','00001']
commands['slt']=['r','000000','00010']
commands['or']= ['r','000000','00011']
commands['and']=['r','000000','00100']
commands['lw']= ['l','000001']
commands['sw']= ['l','000010']
commands['beq']=['l','000011']
commands['bne']=['l','000100']
commands['li']= ['k','000110']
commands['j']=  ['b','000101']
commands['nop']=['n','00000000000000000000000000000000']

# Regex patterns
label_check=re.compile("(\$)([A-Za-z]{1,})(\$)")
comment_check=re.compile("(#)")
inst_parse=re.compile("^[A-Za-z]{1,}")
reg_parse=re.compile("[0-9]{1,}")
space_check=re.compile("^(\s){1,}")
def main():
    # Check for proper arguments
    if len(sys.argv) != 3:
        print('Format is %s source.asm target.coe') %(sys.argv[0])
        exit()
    # Open output file
    bin_file=file(sys.argv[2],'w')
    
    addr_count=0
    labels={}
    # First pass, collect labels (save to a dict with their name as the key, save the addr_count as their address), skip comments and whitespace, for every instruction increment addr_count
    f=file(sys.argv[1],'r')
    for line in f:
        if comment_check.search(line) or space_check.search(line):
            continue
        if inst_parse.search(line):
            addr_count+=1
            continue
        label=label_check.match(line)
        if not label: # Since this is the only other valid thing it can be, we need to quit and notify
            print('Error[%s]') % (str(line))
            exit()
        labels[label.group(2)]=addr_count # Save the address of the next valid instruction to this label value

    f.close()
    # Second pass, replace all labels with the addresses after them (as collected in the first pass).  Parse the instruction, find it's type, form its bitstring, save the bitstring.
    inst_mem=''
    f=file(sys.argv[1],'r')
    for line in f:
        # Check if it is a comment
        if comment_check.search(line) or label_check.match(line) or space_check.search(line):
            continue
        bitstr=''
        print('Orig:'+line)
        # Replace labels
        lab_re=label_check.search(line)
        if lab_re:
            line=line[:lab_re.start()]+str(labels[lab_re.group(2)])+line[lab_re.end():]
            print('Labl:'+line)
        inst_re=inst_parse.search(line)
        inst=inst_re.group()
        inst_type=commands[inst][0]

        # Handle each instruction type, form their bitstring as required by that type
        if inst_type=='r':
            reg_nums=reg_parse.findall(line[inst_re.end():])
            bitstr=commands[inst][1]+dec2bin(reg_nums[1],5)+dec2bin(reg_nums[2],5)+dec2bin(reg_nums[0],5)+dec2bin(0,6)+commands[inst][2]
        elif inst_type=='l':
            reg_nums=reg_parse.findall(line[inst_re.end():])
            bitstr=commands[inst][1]+dec2bin(reg_nums[1],5)+dec2bin(reg_nums[0],5)+dec2bin(reg_nums[2],16)
        elif inst_type=='k':
            reg_nums=reg_parse.findall(line[inst_re.end():])
            bitstr=commands[inst][1]+dec2bin(reg_nums[0],5)+dec2bin(0,5)+dec2bin(reg_nums[1],16)
        elif inst_type=='b':
            reg_nums=reg_parse.findall(line[inst_re.end():])
            bitstr=commands[inst][1]+dec2bin(0,10)+dec2bin(reg_nums[0],16)
        elif inst_type=='n':
            reg_nums=reg_parse.findall(line[inst_re.end():])
            bitstr=commands[inst][1]
        print(bitstr)
        inst_mem+=bitstr
    f.close()
    out_file=file(sys.argv[2],'w')
    # Put the bitstream into the coe file format used in ISE
    inst_mem=formatcoe(inst_mem)
    print(inst_mem)
    out_file.write(inst_mem)
    out_file.close()
if __name__ == "__main__":
    main()
